﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace Crayons.Components
{
    public class Selection
    {
        public enum HandleType //clockwise
        {
            TopLeft,
            Top,
            TopRight,
            Right,
            BottomRight,
            Bottom,
            BottomLeft,
            Left,
            None
        }

        public event EventHandler OnSelectStateChanged;

        private Surface m_surface;
        private Boolean m_selected;
        private Rectangle m_rect;   // position & size of selection in surface's coordinate
        private Point[] m_handles;
        private HandleType m_grabbed;
        
        public Boolean Selected
        {
            get
            {
                return m_selected;
            }

            set
            {
                bool stateChanged = (m_selected != value);
                m_selected = value;
                if (stateChanged && OnSelectStateChanged != null)
                {
                    OnSelectStateChanged(this, new EventArgs());
                }
                
            }
        }

        public Rectangle BoundingRect
        {
            get
            {
                return m_rect;
            }

            set
            {
                m_rect = value;

                //north west
                m_handles[0].X = m_rect.Left;
                m_handles[0].Y = m_rect.Top;

                //north
                m_handles[1].X = (m_rect.Left + m_rect.Right) / 2;
                m_handles[1].Y = m_rect.Top;

                //north-east
                m_handles[2].X = m_rect.Right;
                m_handles[2].Y = m_rect.Top;

                //east
                m_handles[3].X = m_rect.Right;
                m_handles[3].Y = (m_rect.Top + m_rect.Bottom) / 2;

                //south-east
                m_handles[4].X = m_rect.Right;
                m_handles[4].Y = m_rect.Bottom;

                //south
                m_handles[5].X = (m_rect.Left + m_rect.Right) / 2;
                m_handles[5].Y = m_rect.Bottom;

                //south-west
                m_handles[6].X = m_rect.Left;
                m_handles[6].Y = m_rect.Bottom;

                //west
                m_handles[7].X = m_rect.Left;
                m_handles[7].Y = (m_rect.Top + m_rect.Bottom) / 2;

            }
        }

        public HandleType GrabbedHandle
        {
            get
            {
                return m_grabbed;
            }

            set
            {
                m_grabbed = value;
            }
        }

        public Point GrabbedHandlePosition
        {
            set
            {
                if (m_grabbed != HandleType.None)
                {
                    int left = BoundingRect.Left;
                    int top = BoundingRect.Top;
                    int right = BoundingRect.Right;
                    int bottom = BoundingRect.Bottom;

                    if (m_grabbed == HandleType.Left || m_grabbed == HandleType.TopLeft || m_grabbed == HandleType.BottomLeft)
                        left = value.X;

                    if (m_grabbed == HandleType.Top || m_grabbed == HandleType.TopLeft || m_grabbed == HandleType.TopRight)
                        top = value.Y;

                    if (m_grabbed == HandleType.Right || m_grabbed == HandleType.TopRight || m_grabbed == HandleType.BottomRight)
                        right = value.X;

                    if (m_grabbed == HandleType.Bottom || m_grabbed == HandleType.BottomLeft || m_grabbed == HandleType.BottomRight)
                        bottom = value.Y;

                    BoundingRect = Rectangle.FromLTRB(left, top, right, bottom);
                }
            }
        }

        public Selection(Surface belongToSurface)
        {
            m_rect = new Rectangle(0, 0, 0, 0);
            m_surface = belongToSurface;
            m_handles = new Point[8];
        }

        public void Draw(Graphics g)
        {
            Point offset = Point.Empty;
            Double scale = m_surface.ScaleRate;

            float[] dashValues = { 5, 5 };
            Pen blpen = new Pen(Color.FromArgb(51, 153, 255), 1);
            blpen.DashPattern = dashValues;
            SolidBrush bbr = new SolidBrush(Color.FromArgb(51, 153, 255));

            Pen bkpen = new Pen(Color.Black, 1);
            SolidBrush wbr = new SolidBrush(Color.White);

            Rectangle tmpRec = new Rectangle(
                (Int32)(BoundingRect.X * scale) + offset.X,
                (Int32)(BoundingRect.Y * scale) + offset.Y,
                (Int32)(BoundingRect.Width * scale),
                (Int32)(BoundingRect.Height * scale)
            );

            g.DrawLine(blpen, tmpRec.Left, tmpRec.Top, tmpRec.Right, tmpRec.Top);
            g.DrawLine(blpen, tmpRec.Right, tmpRec.Top, tmpRec.Right, tmpRec.Bottom);
            g.DrawLine(blpen, tmpRec.Right, tmpRec.Bottom, tmpRec.Left, tmpRec.Bottom);
            g.DrawLine(blpen, tmpRec.Left, tmpRec.Bottom, tmpRec.Left, tmpRec.Top);

            for (int i = 0; i < m_handles.Length; i++)
            {
                if (m_selected)
                {
                    g.FillRectangle(wbr, (int)(m_handles[i].X * scale) + offset.X - 2, (int)(m_handles[i].Y * scale) + offset.Y - 2, 5, 5);
                    g.DrawRectangle(bkpen, (int)(m_handles[i].X * scale) + offset.X - 2, (int)(m_handles[i].Y * scale) + offset.Y - 2, 4, 4);
                }
                else
                {
                    g.FillRectangle(bbr, (int)(m_handles[i].X * scale) + offset.X - 1, (int)(m_handles[i].Y * scale) + offset.Y - 1, 3, 3);
                }
            }
        }

        public HandleType PickHandle(Point pointToTest)
        {
            if (!m_selected)
                return HandleType.None;

            for (int i = 0; i < m_handles.Length; i++)
            {
                Point pos = m_surface.FromSurfaceLocalCoord(m_handles[i]);
                Rectangle rec = new Rectangle(pos.X - 2, pos.Y - 2, 5, 5);

                if (rec.Contains(pointToTest))
                    return (HandleType)i;

            }
            return HandleType.None;
        }
    }

    public class SelectionCollection
    {
        private Surface m_surface;
        private List<Selection> m_selections;
        private List<Selection> m_selected;
        private Boolean m_multiSelection;

        public int Count
        {
            get
            {
                return m_selections.Count;
            }
        }

        public bool MultiSelection
        {
            get 
            { 
                return m_multiSelection; 
            }

            set
            {
                m_multiSelection = value;
            }
        }

        public Selection this[int idx]
        {
            get 
            { 
                return m_selections[idx]; 
            }

            set
            {
                m_selections[idx] = value;
            }
        }

        public List<Selection> SelectedSelections
        {
            get
            {
                return m_selected;
            }
        }

        public SelectionCollection(Surface attachedSurface)
        {
            m_surface = attachedSurface;
            m_selections = new List<Selection>();
            m_selected = new List<Selection>();
            m_multiSelection = false;
        }

        public void AddSelection(Selection sel)
        {
            if (sel != null && !m_selections.Contains(sel))
            {
                m_selections.Add(sel);
                if (sel.Selected)
                {
                    m_selected.Insert(0, sel);
                    if (!m_multiSelection)
                    {
                        while (m_selected.Count > 1)
                        {
                            Selection rem = m_selected[1];
                            rem.Selected = false;
                            m_selected.Remove(rem);
                        }
                    }
                }
                sel.OnSelectStateChanged += new EventHandler(this.SingleSelection_OnSelectStateChanged);
            }
        }

        public void ClearZero()
        {
            int idx = 0;
            while (idx < m_selections.Count)
            {
                if (m_selections[idx].BoundingRect.Width * m_selections[idx].BoundingRect.Height == 0)
                {
                    m_selected.Remove(m_selections[idx]);
                    m_selections.Remove(m_selections[idx]);
                }
                else
                    idx++;
            }
        }

        public void Flush(Graphics g)
        {
            if (m_selections != null)
            {
                for (int i = 0; i < m_selections.Count; i++)
                {
                    if (m_selections[i].BoundingRect.Height != 0 && m_selections[i].BoundingRect.Width != 0)
                         m_selections[i].Draw(g);
                }
            }
        }

        protected void SingleSelection_OnSelectStateChanged(object sender, EventArgs e)
        {
            Selection s = sender as Selection;
            if (s.Selected)
            {
                while (m_selected.Count > 0)
                {
                    m_selected[0].Selected = false;
                }
                m_selected.Add(s);
            }
            else
            {
                m_selected.Remove(s);
            }
        }
    }
}
